﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;

namespace MyUtils
{
	public static class DelayedDispatcher
	{
		sealed class SchedItem
		{
			public Action Callback;
			public int Ms;
			public Control ControlToInvoke;
		}

		static readonly Queue<SchedItem> _queue = new Queue<SchedItem>();
		static readonly AutoResetEvent _queueNewItem = new AutoResetEvent(false);
		static readonly object _threadCreationSync = new object();
		static Thread _thread;

		static void ThreadWork()
		{
			try
			{
				while (true)
				{
					_queueNewItem.WaitOne();
					while (true)
					{
						SchedItem item;
						lock(_queue)
						{
							if (_queue.Count == 0)
							{
								break;
							}
							item = _queue.Dequeue();
						}
						// TODO: Replace to smart queuing with sorting by timeout
						Thread.Sleep(item.Ms);
						try
						{
							var ctrl = item.ControlToInvoke;
							if (ctrl != null && ctrl.InvokeRequired)
							{
								ctrl.Invoke(item.Callback);
							}
							else
							{
								item.Callback();
							}
						}
						catch (Exception ex1)
						{
							Trace.WriteLine("Dispatching Action: " + ex1);
						}
					}
				}
			}
			catch (Exception ex2)
			{
				Trace.WriteLine("Disparcher Thread: " + ex2);
			}
			finally
			{
				_thread = null;
			}
		}

		public static void Enqueue(int ms, Action act)
		{
			Enqueue(null, ms, act);
		}

		public static void Enqueue(Control ctrlToInvoke, int ms, Action act)
		{
			if (_thread == null)
			{
				lock(_threadCreationSync)
				{
					if (_thread == null)
					{
						_thread = new Thread(ThreadWork)
						          {
						          	Priority = ThreadPriority.AboveNormal,
						          	Name = "Dispatcher",
						          	IsBackground = true,
						          };
						_thread.Start();
					}
				}
			}

			var newItem = new SchedItem
			{
				Callback = act,
				ControlToInvoke = ctrlToInvoke,
				Ms = ms,
			};

			lock(_queue)
			{
				_queue.Enqueue(newItem);
			}

			_queueNewItem.Set();
		}
	}
}